package com.cabal.module.security;

import com.alibaba.fastjson.JSONObject;
import com.cabal.common.util.CabalGatewayMD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;

/**
 * token拦截器
 *
 * @author shenkang
 * @data 2022年4月26日
 */
@Slf4j
@Component(value = "authorizationInterceptor")
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {


    @Value("${md5.secret_key}")
    private String secretKey;

    /**
     * 存放登录用户模型Key的Request Key
     */

    public static final String REQUEST_CURRENT_USER_ID = "REQUEST_CURRENT_USER_ID";

    public static final String REQUEST_CURRENT_ACCOUNT = "REQUEST_CURRENT_ACCOUNT";

    public static final String REQUEST_CURRENT_ACCOUNT_ID = "REQUEST_CURRENT_ACCOUNT_ID";

    public static final String REQUEST_CURRENT_USER_NAME = "REQUEST_CURRENT_USER_NAME";

    public static final String REQUEST_CURRENT_ACCOUNT_TYPE = "REQUEST_CURRENT_ACCOUNT_TYPE";

    public static final String REQUEST_CURRENT_CUSTOM_TEN_CODE = "REQUEST_CURRENT_CUSTOM_TEN_CODE";

    public static final String CONTENT_TYPE_KEY = "Content-Type";

    public static final String APPLICATION_TYPE = "application/json";

    public static final String APPLICATION_TYPE_CHAR = "application/json;charset=utf-8";

    public static final String SIGN_FIELD = "sign";

    public static final String REQUEST_GET = "GET";



    /**
     *  存放鉴权信息的Header名称，默认是Authorization
     */
    private String httpHeaderName = "Authorization";


    /**
     *  鉴权失败后返回的错误信息，默认为401 unauthorized
     */
    private String unauthorizedErrorMessage = "401 unauthorized";

    /**
     *  鉴权失败后返回的HTTP错误码，默认为401
     */
    private int unauthorizedErrorCode = HttpServletResponse.SC_UNAUTHORIZED;


    @Value("${verify-token}")
    private boolean verifyToken;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(!verifyToken) {
            return true;
        }

        HandlerMethod handlerMethod;
        try {
            handlerMethod = (HandlerMethod) handler;
        }catch (Exception e){
            return false;
        }
        Method method = handlerMethod.getMethod();

        if ((handlerMethod.getBeanType().getAnnotation(SecurityAdminNoCheck.class) != null
                || method.getAnnotation(SecurityAdminNoCheck.class) != null)){
            return true;
        }

        if ((handlerMethod.getBeanType().getAnnotation(SecurityValidMd5SignCheck.class) != null
                || method.getAnnotation(SecurityValidMd5SignCheck.class) != null)){
            String contentType = request.getHeader(CONTENT_TYPE_KEY);
            if(APPLICATION_TYPE.equalsIgnoreCase(contentType) || APPLICATION_TYPE_CHAR.equalsIgnoreCase(contentType)){
                BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
                String body = IOUtils.toString(reader);
                JSONObject jsonObject = JSONObject.parseObject(body);
                String sign = jsonObject.getString(SIGN_FIELD);

                // 进行MD5签名值校验
                String md5Value = CabalGatewayMD5Util.jsonMd5(body, "sign", secretKey);
                if(!md5Value.equals(sign)) {
                    log.info("验签参数>>>>{}", body);
                    log.info(handlerMethod.getMethod().getName() + "签名不通过，签名值>>>>" + md5Value);
                    return false;
                }
                //log.info(handlerMethod.getMethod().getName() + "验签SUCCESS");

            } else {
                if(REQUEST_GET.equalsIgnoreCase(request.getMethod())) {
                    String sign = request.getParameter(SIGN_FIELD);
                    String timestampParam = request.getParameter("timestamp");
                    String md5Value = CabalGatewayMD5Util.stringParamsMd5(timestampParam, secretKey);
                    if(!md5Value.equals(sign)) {
                        log.info(handlerMethod.getMethod().getName() + "签名不通过，签名值>>>>" + md5Value);
                        return false;
                    }
                    //log.info(handlerMethod.getMethod().getName() + "验签SUCCESS");
                }
            }
            return true;
        }


        String unauthorizedErrorMsg = unauthorizedErrorMessage;

        // 从header中得到token
        String token = request.getHeader(httpHeaderName);
        if(StringUtils.isBlank(token)) {
            token = request.getParameter("Authorization");
        }
        boolean flag = true;
        /*if (token != null && token.length() > 0) {
            Jws<Claims> claimsJws = null;
            try {

                claimsJws = Jwts.parser().setSigningKey(AbstractTokenManager.TOKEN_KEY).parseClaimsJws(token);
                // OK, we can trust this JWT

                //查询用户状态
                String md5 = CabalGatewayMD5Util.encodeByMD5(token);
                Integer count = tokenLogMapper.selectCount(md5);
                if(count == null ||  count== 0){
                    unauthorizedErrorMsg ="Token expired, please log in again";
                    flag = false;
                }

            } catch (SignatureException | MalformedJwtException | ExpiredJwtException e) {
                // don't trust the JWT!
                log.warn("Wrong token: {}", token);
                unauthorizedErrorMsg ="Token authorization failed";
            }

            if (claimsJws != null && flag) {
                String userId = claimsJws.getBody().getAudience();
                String account = claimsJws.getBody().getSubject();
                String userName = claimsJws.getBody().get("user_name").toString();
                String loginAccountId = claimsJws.getBody().get("login_account_id").toString();
                Integer accountType = Integer.parseInt(claimsJws.getBody().get("account_type").toString());
                String updateSeconds = claimsJws.getBody().get("updateSeconds").toString();
                String customTenCode = claimsJws.getBody().get("custom_ten_code").toString();
                Date now = Calendar.getInstance().getTime();
                long nowTime = now.getTime();
                long updateSecondsTime = Long.valueOf(updateSeconds);
                if(nowTime > updateSecondsTime){
                    String md5 = CabalGatewayMD5Util.encodeByMD5(token);
                    tokenLogMapper.deleteByToken(md5);

                    LoginTokenVo loginTokenVo = new LoginTokenVo();
                    loginTokenVo.setUserId(StringUtils.isBlank(userId) ? null : Long.parseLong(userId));
                    loginTokenVo.setLoginAccountId(Long.parseLong(loginAccountId));
                    loginTokenVo.setAccount(account);
                    loginTokenVo.setUserName(userName);
                    loginTokenVo.setAccountType(accountType);
                    loginTokenVo.setCustomTenCode(customTenCode);
                    token = tokenManager.createToken(loginTokenVo);
                    response.setHeader("Access-Control-Expose-Headers",httpHeaderName);
                    response.setHeader(httpHeaderName,token);
                }

                if (StringUtils.isNotBlank(account)) {
                    // 如果token验证成功，将token对应的用户id存在request中，便于之后注入
                    request.setAttribute(REQUEST_CURRENT_USER_ID, userId);
                    request.setAttribute(REQUEST_CURRENT_ACCOUNT, account);
                    request.setAttribute(REQUEST_CURRENT_ACCOUNT_ID, loginAccountId);
                    request.setAttribute(REQUEST_CURRENT_USER_NAME, userName);
                    request.setAttribute(REQUEST_CURRENT_ACCOUNT_TYPE, accountType);
                    request.setAttribute(REQUEST_CURRENT_CUSTOM_TEN_CODE, customTenCode);

                    LoginUserInfoVo loginUserInfoVo = new LoginUserInfoVo();
                    loginUserInfoVo.setLoginAccount(account);
                    loginUserInfoVo.setUserId(StringUtils.isBlank(userId) ? null : Long.parseLong(userId));
                    loginUserInfoVo.setUserName(userName);
                    loginUserInfoVo.setAccountType(accountType);
                    loginUserInfoVo.setLoginAccountId(Long.parseLong(loginAccountId));
                    loginUserInfoVo.setCustomTenCode(customTenCode);
                    userInfoThreadLocal.set(loginUserInfoVo);
                    return true;

                }else {
                    unauthorizedErrorMsg ="Token expired, please log in again";
                }
            }

        }*/
        response.setStatus(unauthorizedErrorCode);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
        writer.write("{\n" +
                "  \"success\": false,\n" +
                "  \"code\": \"401\",\n" +
                "  \"message\": \""+unauthorizedErrorMsg+"\",\n" +
                "  \"time\": \""+System.currentTimeMillis()+"\",\n"+
                "  \"data\": null,\n" +
                "  \"errors\": null,\n" +
                "  \"stackTrace\": null\n" +
                "}");
        writer.close();
        return false;
    }

    public String getHttpHeaderName() {
        return httpHeaderName;
    }

    public void setHttpHeaderName(String httpHeaderName) {
        this.httpHeaderName = httpHeaderName;
    }

    public String getUnauthorizedErrorMessage() {
        return unauthorizedErrorMessage;
    }

    public void setUnauthorizedErrorMessage(String unauthorizedErrorMessage) {
        this.unauthorizedErrorMessage = unauthorizedErrorMessage;
    }

    public int getUnauthorizedErrorCode() {
        return unauthorizedErrorCode;
    }

    public void setUnauthorizedErrorCode(int unauthorizedErrorCode) {
        this.unauthorizedErrorCode = unauthorizedErrorCode;
    }

    public static String getRequestCurrentUserId() {
        return REQUEST_CURRENT_USER_ID;
    }

    public static String getRequestCurrentAccount() {
        return REQUEST_CURRENT_ACCOUNT;
    }
}
